package com.yricky.astral.signal

import com.yricky.astral.lib.VerilogKeyword.ASSIGN
import com.yricky.astral.project.Hazuki
import com.yricky.astral.structure.INetList
import com.yricky.astral.structure.AstralModule

/**
 * Don't use this directly!
 * @see AstralModule.wire
 */
class Wire(override val name: String, override val width: Int,private val _tags:Collection<String>?,
           override val netList: INetList
) :AssignableSignal,
    Hazuki.Checkable by netList {
    init {
        if(width >64){
            standard.check(Hazuki.Rules.WidthTooLarge,"Width of wire signal $name is $width",Hazuki.CheckLevel.allow)
        }
    }

    override fun level(): Int = sourceSignal?.level() ?: 0
    override var sourceSignal:Signal? = null

    override fun generateAssignTo():String{
        return sourceSignal?.generateBody() ?: ""
    }

    override fun assignTo(s: Signal):AssignableSignal {
        if(tags.contains(Signal.TAG_INPUT))
            throw IllegalArgumentException("Wire signal \"$name\" cannot be assigned!")
        return super.assignTo(s)
    }

    override val tags: MutableSet<String> by lazy {
        HashSet<String>().also {
            it.add(Signal.TAG_WIRE)
            if(_tags != null){ it.addAll(_tags) }
        }
    }

    override fun generateLogic(): String {
        return if(sourceSignal!=null) "$ASSIGN $name = ${generateAssignTo()};\n" else ""
    }

    override fun generateDeclare(): String {
        val widthInfo = if(width>1) "[${width-1}:0]" else ""
        return "wire$widthInfo $name;\n"
    }

}